//*******************************************************************
//	created:	21:4:2011   22:32
//	filename: 	platformhelper.cpp
//	author:		tiamo
//	purpose:	platform helper
//*******************************************************************

#include "stdafx.h"
#include "platformhelper.h"

namespace platform
{
	//
	// get current tick count
	//
	uint32_t getCurrentTick()
	{
	#ifdef WIN32
		return GetTickCount();
	#else
		timespec ts;
		if(clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
			ThrowException(L"unable to get clock time (%u)", getLastErrorCode());

		return static_cast<uint32_t>(ts.tv_sec * 1000 + ts.tv_nsec / 1000 / 1000);
	#endif
	}

	//
	// get last error
	//
	uint32_t getLastErrorCode()
	{
	#ifdef WIN32
		return GetLastError();
	#else
		return errno;
	#endif
	}

	//
	// get local time
	//
	tm getLocalTime(time_t theTime)
	{
		tm theTm;
	#ifdef WIN32
		localtime_s(&theTm, &theTime);
	#else
		theTm																= *localtime(&theTime);
	#endif
		return theTm;
	}

	//
	// string -> time
	//
	uint32_t stringToTime(UniString const& stringTime)
	{
		tm theTm															= {0};
	#ifdef WIN32
		swscanf_s(stringTime.c_str(), L"%04d-%04d-%04d %02d:%02d:%02d", &theTm.tm_year, &theTm.tm_mon, &theTm.tm_mday, &theTm.tm_hour, &theTm.tm_min, &theTm.tm_sec);
	#else
		swscanf(stringTime.c_str(), L"%04d-%04d-%04d %02d:%02d:%02d", &theTm.tm_year, &theTm.tm_mon, &theTm.tm_mday, &theTm.tm_hour, &theTm.tm_min, &theTm.tm_sec);
	#endif
		theTm.tm_year														-= 1900;
		theTm.tm_mon														-= 1;
		return static_cast<uint32_t>(mktime(&theTm));
	}

	//
	// time -> string
	//
	UniString timeToString(uint32_t unixTime)
	{
		tm localTime														= getLocalTime(unixTime);
		wchar_t timeString[64];
		snwprintf(timeString, ARRAYSIZE(timeString), L"%04d-%02d-%02d %02d:%02d:%02d", localTime.tm_year + 1900, localTime.tm_mon + 1, localTime.tm_mday, localTime.tm_hour, localTime.tm_min, localTime.tm_sec);
		return timeString;
	}

	//
	// sleep
	//
	void sleep(uint32_t milliSeconds)
	{
	#ifdef WIN32
		::Sleep(milliSeconds);
	#else
		usleep(milliSeconds * 1000);
	#endif
	}

	//
	// vsnprintf
	//
	int vsnwprintf(wchar_t* theBuffer, size_t bufferSize, wchar_t const* format, va_list argList)
	{
	#ifdef WIN32
		return _vsnwprintf_s(theBuffer, bufferSize, _TRUNCATE, format, argList);
	#else
		return vswprintf(theBuffer, bufferSize, format, argList);
	#endif
	}

	//
	// string -> time(rfc2822)
	//
	uint32_t rfc2822StringToTime(UniString const& stringTime)
	{
		tm t																= {0};
		wchar_t dayOfWeek[4]												= {0};
		wchar_t month[4]													= {0};
		wchar_t timeZone[8]													= {0};
	#ifdef WIN32
		if(swscanf_s(stringTime.c_str(), L"%3ls, %02d %3ls %d %02d:%02d:%02d %ls", dayOfWeek, ARRAYSIZE(dayOfWeek), &t.tm_mday, month, ARRAYSIZE(month), &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, timeZone, ARRAYSIZE(timeZone)) < 7)
	#else
		if(swscanf(stringTime.c_str(), L"%3ls, %02d %3ls %d %02d:%02d:%02d %ls", dayOfWeek, &t.tm_mday, month, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, timeZone) < 7)
	#endif
			return 0;

		static wchar_t const* dayName[]										= {L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat"};
		static wchar_t const* monthName[]									= {L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"};
		static wchar_t const* zoneName[]									= {L"UT", L"GMT", L"EST", L"EDT", L"CST", L"CDT", L"MST", L"MDT", L"PST", L"PDT"};
		static int zoneOffset[]												= {    0,      0,   -300,   -240,   -360,   -300,   -420,   -360,   -480,   -420};
		static wchar_t const* zoneName2[]									= {L"A", L"B", L"C", L"D", L"E", L"F", L"G", L"H", L"I", L"K", L"L", L"M", L"N", L"O", L"P", L"Q", L"R", L"S", L"T", L"U", L"V", L"W", L"X", L"Y", L"Z"};
		static int zoneOffset2[]											= {  60,  120,  180,  240,  300,  360,  420,  480,  540,  600,  660,  720,  -60, -120, -180, -240, -300, -360, -420, -480, -540, -600, -660, -720,    0};

		auto search = [](wchar_t const* itemList[], int itemCount, wchar_t const* theValue, int notFound) -> int
		{
			for(int i = 0; i < itemCount; i ++)
			{
				if(!wcsicmp(itemList[i], theValue))
					return i;
			}

			return notFound;
		};
		t.tm_wday															= search(dayName, ARRAYSIZE(dayName), dayOfWeek, 0);
		t.tm_mon															= search(monthName, ARRAYSIZE(monthName), month, 0);
		t.tm_year															-= 1900;

		int offset															= search(zoneName, ARRAYSIZE(zoneName), timeZone, 0x7fffffff);
		if(offset == 0x7fffffff)
		{
			offset															= search(zoneName2, ARRAYSIZE(zoneName2), timeZone, 0x7fffffff);
			if(offset == 0x7fffffff)
			{
				int minutes													= 0;
				int hours													= 0;
				int plusOrMinus												= 0;
			#ifdef WIN32
				if(3 == swscanf_s(timeZone, L"%c%2d%2d", &plusOrMinus, 1, &hours, &minutes))
			#else
				if(3 == swscanf(timeZone, L"%c%02d%02d", &plusOrMinus, &hours, &minutes))
			#endif
					offset													= (plusOrMinus == L'+' ? 1 : -1) * (hours * 60 + minutes);
				else
					offset													= 0;
			}
			else
			{
				offset														= zoneOffset2[offset];
			}
		}
		else
		{
			offset															= zoneOffset[offset] * 60;;
		}

	#ifdef WIN32
		return static_cast<uint32_t>(_mkgmtime(&t) - offset * 60);
	#else
		return static_cast<uint32_t>(timegm(&t) - offset * 60);
	#endif
	}

	//
	// snprintf
	//
	int snwprintf(wchar_t* theBuffer, size_t bufferSize, wchar_t const* format, ...)
	{
		va_list argList;
		va_start(argList, format);
		int length															= vsnwprintf(theBuffer, bufferSize, format, argList);
		va_end(argList);

		return length;
	}

	//
	// wcsncasecmp
	//
	int wcsnicmp(wchar_t const* s1, wchar_t const* s2, size_t maxCount)
	{
	#ifdef WIN32
		return _wcsnicmp(s1, s2, maxCount);
	#else
		return wcsncasecmp(s1, s2, maxCount);
	#endif
	}

	//
	// wcscasecmp
	//
	int wcsicmp(wchar_t const* s1, wchar_t const* s2)
	{
	#ifdef WIN32
		return _wcsicmp(s1, s2);
	#else
		return wcscasecmp(s1, s2);
	#endif
	}

	//
	// vsnprintf
	//
	int vsnprintf(char* theBuffer, size_t bufferSize, char const* format, va_list argList)
	{
	#ifdef WIN32
		return _vsnprintf_s(theBuffer, bufferSize, _TRUNCATE, format, argList);
	#else
		return ::vsnprintf(theBuffer, bufferSize, format, argList);
	#endif
	}

	//
	// snprintf
	//
	int snprintf(char* theBuffer, size_t bufferSize, char const* format, ...)
	{
		va_list argList;
		va_start(argList, format);
		int length															= vsnprintf(theBuffer, bufferSize, format, argList);
		va_end(argList);

		return length;
	}

	//
	// atoi64
	//
	int64_t atoi64(char const* s)
	{
	#ifdef WIN32
		return _atoi64(s);
	#else
		return static_cast<int64_t>(atoll(s));
	#endif
	}

	//
	// wcstoi64
	//
	int64_t wcstoi64(wchar_t const* ptr, wchar_t** end, int base)
	{
	#ifdef WIN32
		return _wcstoi64(ptr, end, base);
	#else
		return wcstoll(ptr, end, base);
	#endif
	}
}
